import os
from pathlib import Path
import uuid
import subprocess
from chernc.compiler.project.compilation import CompilationContext
from chernc.utils.file_utils import read_file, write_file
from typing import List
from chernc.compiler.project.compilation import FileInfo

class RustCompilationContext(CompilationContext):
    def __init__(self, temp_dir: str = "./temp_dir"):
        self.temp_dir = temp_dir
        os.makedirs(self.temp_dir, exist_ok=True)
        self.project_id = str(uuid.uuid4())
        self.project_name = f"project_{self.project_id}"
        self.project_dir = os.path.join(self.temp_dir, self.project_name)
        self.init_project()

    def init_project(self) -> str:
        if os.path.exists(self.project_dir):
            raise FileExistsError(f"Project {self.project_name} already exists")
        try:
            output = subprocess.check_output(f"cargo new {self.project_dir} --name {self.project_name}", shell=True, stderr=subprocess.STDOUT, universal_newlines=True, timeout=60)
        except subprocess.CalledProcessError as e:
            return e.output
        except subprocess.TimeoutExpired as e:
            return e.output
        if isinstance(output, bytes):
            output = output.decode("utf-8")
        return output
    
    def list_files(self, path: str = "") -> List[FileInfo]:
        # check if absolute path
        if os.path.isabs(path):
            raise ValueError(f"File path {path} is absolute")
        # check ..
        if ".." in path:
            raise ValueError(f"File path {path} contains invalid path")

        final_path = os.path.join(self.project_dir, path)
        files = []
        for file in os.listdir(final_path):
            files.append(FileInfo(os.path.join(path, file), os.path.isdir(os.path.join(final_path, file))))
        return files
    
    def read_file(self, file_path: str) -> str:
        # check if absolute path
        if os.path.isabs(file_path):
            raise ValueError(f"File path {file_path} is absolute")
        # check ..
        if ".." in file_path:
            raise ValueError(f"File path {file_path} contains invalid path")
        final_file_path = os.path.join(self.project_dir, file_path)
        # check if file exists
        if not os.path.exists(final_file_path):
            raise FileNotFoundError(f"File {file_path} not found")
        return read_file(final_file_path)
    
    def write_file(self, file_path: str, content: str) -> bool:
        # check if absolute path
        if os.path.isabs(file_path):
            raise ValueError(f"File path {file_path} is absolute")
        # check ..
        if ".." in file_path:
            raise ValueError(f"File path {file_path} contains invalid path")
        final_file_path = os.path.join(self.project_dir, file_path)
        try:
            Path(final_file_path).write_text(content, encoding="utf-8")
        except:
            return False
        return True
    
    def create_file(self, file_path: str) -> bool:
        # check if absolute path
        if os.path.isabs(file_path):
            raise ValueError(f"File path {file_path} is absolute")
        # check ..
        if ".." in file_path:
            raise ValueError(f"File path {file_path} contains invalid path")
        final_file_path = os.path.join(self.project_dir, file_path)
        with open(final_file_path, "w") as f:
            pass
        return True
    
    def delete_file(self, file_path: str) -> bool:
        # check if absolute path
        if os.path.isabs(file_path):
            raise ValueError(f"File path {file_path} is absolute")
        # check ..
        if ".." in file_path:
            raise ValueError(f"File path {file_path} contains invalid path")
        final_file_path = os.path.join(self.project_dir, file_path)
        os.remove(final_file_path)
        return not os.path.exists(final_file_path)
    
    def rename_file(self, old_file_path: str, new_file_path: str) -> bool:
        # check if absolute path
        if os.path.isabs(old_file_path) or os.path.isabs(new_file_path):
            raise ValueError(f"File path {old_file_path} or {new_file_path} is absolute")
        # check ..
        if ".." in old_file_path or ".." in new_file_path:
            raise ValueError(f"File path {old_file_path} or {new_file_path} contains invalid path")
        final_old_file_path = os.path.join(self.project_dir, old_file_path)
        final_new_file_path = os.path.join(self.project_dir, new_file_path)
        os.rename(final_old_file_path, final_new_file_path)
        return not os.path.exists(final_old_file_path) and os.path.exists(final_new_file_path)

    def compile(self) -> str:
        try:
            output = subprocess.check_output(f"cargo build", cwd=self.project_dir, shell=True, stderr=subprocess.STDOUT, universal_newlines=True, timeout=60)
        except subprocess.CalledProcessError as e:
            return e.output
        except subprocess.TimeoutExpired as e:
            return e.output
        if isinstance(output, bytes):
            output = output.decode("utf-8")
        return output
    
    def run(self) -> str:
        try:
            output = subprocess.check_output(f"cargo run", cwd=self.project_dir, shell=True, stderr=subprocess.STDOUT, universal_newlines=True, timeout=60)
        except subprocess.CalledProcessError as e:
            return e.output
        except subprocess.TimeoutExpired as e:
            return e.output
        if isinstance(output, bytes):
            output = output.decode("utf-8")
        return output
    
    def clean(self) -> None:
        pass